home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / tk2.3 / dist / tkListbox.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-05-13  |  52.0 KB  |  1,786 lines

  1. /* 
  2.  * tkListbox.c --
  3.  *
  4.  *    This module implements listbox widgets for the Tk
  5.  *    toolkit.  A listbox displays a collection of strings,
  6.  *    one per line, and provides scrolling and selection.
  7.  *
  8.  * Copyright 1990-1992 Regents of the University of California.
  9.  * Permission to use, copy, modify, and distribute this
  10.  * software and its documentation for any purpose and without
  11.  * fee is hereby granted, provided that the above copyright
  12.  * notice appear in all copies.  The University of California
  13.  * makes no representations about the suitability of this
  14.  * software for any purpose.  It is provided "as is" without
  15.  * express or implied warranty.
  16.  */
  17.  
  18. #ifndef lint
  19. static char rcsid[] = "$Header: /user6/ouster/wish/RCS/tkListbox.c,v 1.56 92/05/13 09:05:20 ouster Exp $ SPRITE (Berkeley)";
  20. #endif
  21.  
  22. #include "tkConfig.h"
  23. #include "default.h"
  24. #include "tkInt.h"
  25.  
  26. /*
  27.  * One record of the following type is kept for each element
  28.  * associated with a listbox widget:
  29.  */
  30.  
  31. typedef struct Element {
  32.     int textLength;        /* # non-NULL characters in text. */
  33.     int lBearing;        /* Distance from first character's
  34.                  * origin to left edge of character. */
  35.     int pixelWidth;        /* Total width of element in pixels (including
  36.                  * left bearing and right bearing). */
  37.     struct Element *nextPtr;    /* Next in list of all elements of this
  38.                  * listbox, or NULL for last element. */
  39.     char text[4];        /* Characters of this element, NULL-
  40.                  * terminated.  The actual space allocated
  41.                  * here will be as large as needed (> 4,
  42.                  * most likely).  Must be the last field
  43.                  * of the record. */
  44. } Element;
  45.  
  46. #define ElementSize(stringLength) \
  47.     ((unsigned) (sizeof(Element) - 3 + stringLength))
  48.  
  49. /*
  50.  * A data structure of the following type is kept for each listbox
  51.  * widget managed by this file:
  52.  */
  53.  
  54. typedef struct {
  55.     Tk_Window tkwin;        /* Window that embodies the listbox.  NULL
  56.                  * means that the window has been destroyed
  57.                  * but the data structures haven't yet been
  58.                  * cleaned up.*/
  59.     Tcl_Interp *interp;        /* Interpreter associated with listbox. */
  60.     int numElements;        /* Total number of elements in this listbox. */
  61.     Element *elementPtr;    /* First in list of elements (NULL if no
  62.                  * elements. */
  63.  
  64.     /*
  65.      * Information used when displaying widget:
  66.      */
  67.  
  68.     Tk_3DBorder normalBorder;    /* Used for drawing border around whole
  69.                  * window, plus used for background. */
  70.     int borderWidth;        /* Width of 3-D border around window. */
  71.     int relief;            /* 3-D effect: TK_RELIEF_RAISED, etc. */
  72.     XFontStruct *fontPtr;    /* Information about text font, or NULL. */
  73.     XColor *fgColorPtr;        /* Text color in normal mode. */
  74.     GC textGC;            /* For drawing normal text. */
  75.     Tk_3DBorder selBorder;    /* Borders and backgrounds for selected
  76.                  * elements. */
  77.     int selBorderWidth;        /* Width of border around selection. */
  78.     XColor *selFgColorPtr;    /* Foreground color for selected elements. */
  79.     GC selTextGC;        /* For drawing selected text. */
  80.     char *geometry;        /* Desired geometry for window.  Malloc'ed. */
  81.     int lineHeight;        /* Number of pixels allocated for each line
  82.                  * in display. */
  83.     int topIndex;        /* Index of top-most element visible in
  84.                  * window. */
  85.     int numLines;        /* Number of lines (elements) that fit
  86.                  * in window at one time. */
  87.  
  88.     /*
  89.      * Information to support horizontal scrolling:
  90.      */
  91.  
  92.     int maxWidth;        /* Width (in pixels) of widest string in
  93.                  * listbox. */
  94.     int xScrollUnit;        /* Number of pixels in one "unit" for
  95.                  * horizontal scrolling (window scrolls
  96.                  * horizontally in increments of this size).
  97.                  * This is an average character size. */
  98.     int xOffset;        /* The left edge of each string in the
  99.                  * listbox is offset to the left by this
  100.                  * many pixels (0 means no offset, positive
  101.                  * means there is an offset). */
  102.  
  103.     /*
  104.      * Information about what's selected, if any.
  105.      */
  106.  
  107.     int selectFirst;        /* Index of first selected element (-1 means
  108.                  * nothing selected. */
  109.     int selectLast;        /* Index of last selected element. */
  110.     int selectAnchor;        /* Fixed end of selection (i.e. element
  111.                  * at which selection was started.) */
  112.     int exportSelection;    /* Non-zero means tie internal listbox
  113.                  * to X selection. */
  114.  
  115.     /*
  116.      * Information for scanning:
  117.      */
  118.  
  119.     int scanMarkX;        /* X-position at which scan started (e.g.
  120.                  * button was pressed here). */
  121.     int scanMarkY;        /* Y-position at which scan started (e.g.
  122.                  * button was pressed here). */
  123.     int scanMarkXOffset;    /* Value of "xOffset" field when scan
  124.                  * started. */
  125.     int scanMarkYIndex;        /* Index of line that was at top of window
  126.                  * when scan started. */
  127.  
  128.     /*
  129.      * Miscellaneous information:
  130.      */
  131.  
  132.     Cursor cursor;        /* Current cursor for window, or None. */
  133.     char *yScrollCmd;        /* Command prefix for communicating with
  134.                  * vertical scrollbar.  NULL means no command
  135.                  * to issue.  Malloc'ed. */
  136.     char *xScrollCmd;        /* Command prefix for communicating with
  137.                  * horizontal scrollbar.  NULL means no command
  138.                  * to issue.  Malloc'ed. */
  139.     int flags;            /* Various flag bits:  see below for
  140.                  * definitions. */
  141. } Listbox;
  142.  
  143. /*
  144.  * Flag bits for buttons:
  145.  *
  146.  * REDRAW_PENDING:        Non-zero means a DoWhenIdle handler
  147.  *                has already been queued to redraw
  148.  *                this window.
  149.  * UPDATE_V_SCROLLBAR:        Non-zero means vertical scrollbar needs
  150.  *                to be updated.
  151.  * UPDATE_H_SCROLLBAR:        Non-zero means horizontal scrollbar needs
  152.  *                to be updated.
  153.  */
  154.  
  155. #define REDRAW_PENDING        1
  156. #define UPDATE_V_SCROLLBAR    2
  157. #define UPDATE_H_SCROLLBAR    4
  158.  
  159. /*
  160.  * Information used for argv parsing:
  161.  */
  162.  
  163. static Tk_ConfigSpec configSpecs[] = {
  164.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  165.     DEF_LISTBOX_BG_COLOR, Tk_Offset(Listbox, normalBorder),
  166.     TK_CONFIG_COLOR_ONLY},
  167.     {TK_CONFIG_BORDER, "-background", "background", "Background",
  168.     DEF_LISTBOX_BG_MONO, Tk_Offset(Listbox, normalBorder),
  169.     TK_CONFIG_MONO_ONLY},
  170.     {TK_CONFIG_SYNONYM, "-bd", "borderWidth", (char *) NULL,
  171.     (char *) NULL, 0, 0},
  172.     {TK_CONFIG_SYNONYM, "-bg", "background", (char *) NULL,
  173.     (char *) NULL, 0, 0},
  174.     {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth",
  175.     DEF_LISTBOX_BORDER_WIDTH, Tk_Offset(Listbox, borderWidth), 0},
  176.     {TK_CONFIG_ACTIVE_CURSOR, "-cursor", "cursor", "Cursor",
  177.     DEF_LISTBOX_CURSOR, Tk_Offset(Listbox, cursor), TK_CONFIG_NULL_OK},
  178.     {TK_CONFIG_BOOLEAN, "-exportselection", "exportSelection",
  179.     "ExportSelection", DEF_LISTBOX_EXPORT_SELECTION,
  180.     Tk_Offset(Listbox, exportSelection), 0},
  181.     {TK_CONFIG_SYNONYM, "-fg", "foreground", (char *) NULL,
  182.     (char *) NULL, 0, 0},
  183.     {TK_CONFIG_FONT, "-font", "font", "Font",
  184.     DEF_LISTBOX_FONT, Tk_Offset(Listbox, fontPtr), 0},
  185.     {TK_CONFIG_COLOR, "-foreground", "foreground", "Foreground",
  186.     DEF_LISTBOX_FG, Tk_Offset(Listbox, fgColorPtr), 0},
  187.     {TK_CONFIG_STRING, "-geometry", "geometry", "Geometry",
  188.     DEF_LISTBOX_GEOMETRY, Tk_Offset(Listbox, geometry), 0},
  189.     {TK_CONFIG_RELIEF, "-relief", "relief", "Relief",
  190.     DEF_LISTBOX_RELIEF, Tk_Offset(Listbox, relief), 0},
  191.     {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
  192.     DEF_LISTBOX_SELECT_COLOR, Tk_Offset(Listbox, selBorder),
  193.     TK_CONFIG_COLOR_ONLY},
  194.     {TK_CONFIG_BORDER, "-selectbackground", "selectBackground", "Foreground",
  195.     DEF_LISTBOX_SELECT_MONO, Tk_Offset(Listbox, selBorder),
  196.     TK_CONFIG_MONO_ONLY},
  197.     {TK_CONFIG_PIXELS, "-selectborderwidth", "selectBorderWidth", "BorderWidth",
  198.     DEF_LISTBOX_SELECT_BD, Tk_Offset(Listbox, selBorderWidth), 0},
  199.     {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
  200.     DEF_LISTBOX_SELECT_FG_COLOR, Tk_Offset(Listbox, selFgColorPtr),
  201.     TK_CONFIG_COLOR_ONLY},
  202.     {TK_CONFIG_COLOR, "-selectforeground", "selectForeground", "Background",
  203.     DEF_LISTBOX_SELECT_FG_MONO, Tk_Offset(Listbox, selFgColorPtr),
  204.     TK_CONFIG_MONO_ONLY},
  205.     {TK_CONFIG_STRING, "-xscrollcommand", "xScrollCommand", "ScrollCommand",
  206.     DEF_LISTBOX_SCROLL_COMMAND, Tk_Offset(Listbox, xScrollCmd), 0},
  207.     {TK_CONFIG_STRING, "-yscrollcommand", "yScrollCommand", "ScrollCommand",
  208.     DEF_LISTBOX_SCROLL_COMMAND, Tk_Offset(Listbox, yScrollCmd), 0},
  209.     {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL,
  210.     (char *) NULL, 0, 0}
  211. };
  212.  
  213. /*
  214.  * Forward declarations for procedures defined later in this file:
  215.  */
  216.  
  217. static void        ChangeListboxOffset _ANSI_ARGS_((Listbox *listPtr,
  218.                 int offset));
  219. static void        ChangeListboxView _ANSI_ARGS_((Listbox *listPtr,
  220.                 int index));
  221. static int        ConfigureListbox _ANSI_ARGS_((Tcl_Interp *interp,
  222.                 Listbox *listPtr, int argc, char **argv,
  223.                 int flags));
  224. static void        DeleteEls _ANSI_ARGS_((Listbox *listPtr, int first,
  225.                 int last));
  226. static void        DestroyListbox _ANSI_ARGS_((ClientData clientData));
  227. static void        DisplayListbox _ANSI_ARGS_((ClientData clientData));
  228. static int        GetListboxIndex _ANSI_ARGS_((Tcl_Interp *interp,
  229.                 Listbox *listPtr, char *string, int *indexPtr));
  230. static void        InsertEls _ANSI_ARGS_((Listbox *listPtr, int index,
  231.                 int argc, char **argv));
  232. static void        ListboxComputeWidths _ANSI_ARGS_((Listbox *listPtr,
  233.                 int fontChanged));
  234. static void        ListboxEventProc _ANSI_ARGS_((ClientData clientData,
  235.                 XEvent *eventPtr));
  236. static int        ListboxFetchSelection _ANSI_ARGS_((
  237.                 ClientData clientData, int offset, char *buffer,
  238.                 int maxBytes));
  239. static void        ListboxLostSelection _ANSI_ARGS_((
  240.                 ClientData clientData));
  241. static void        ListboxRedrawRange _ANSI_ARGS_((Listbox *listPtr,
  242.                 int first, int last));
  243. static void        ListboxScanTo _ANSI_ARGS_((Listbox *listPtr,
  244.                 int x, int y));
  245. static void        ListboxSelectFrom _ANSI_ARGS_((Listbox *listPtr,
  246.                 int index));
  247. static void        ListboxSelectTo _ANSI_ARGS_((Listbox *listPtr,
  248.                 int index));
  249. static void        ListboxUpdateHScrollbar _ANSI_ARGS_((Listbox *listPtr));
  250. static void        ListboxUpdateVScrollbar _ANSI_ARGS_((Listbox *listPtr));
  251. static int        ListboxWidgetCmd _ANSI_ARGS_((ClientData clientData,
  252.                 Tcl_Interp *interp, int argc, char **argv));
  253. static int        NearestListboxElement _ANSI_ARGS_((Listbox *listPtr,
  254.                 int y));
  255.  
  256. /*
  257.  *--------------------------------------------------------------
  258.  *
  259.  * Tk_ListboxCmd --
  260.  *
  261.  *    This procedure is invoked to process the "listbox" Tcl
  262.  *    command.  See the user documentation for details on what
  263.  *    it does.
  264.  *
  265.  * Results:
  266.  *    A standard Tcl result.
  267.  *
  268.  * Side effects:
  269.  *    See the user documentation.
  270.  *
  271.  *--------------------------------------------------------------
  272.  */
  273.  
  274. int
  275. Tk_ListboxCmd(clientData, interp, argc, argv)
  276.     ClientData clientData;    /* Main window associated with
  277.                  * interpreter. */
  278.     Tcl_Interp *interp;        /* Current interpreter. */
  279.     int argc;            /* Number of arguments. */
  280.     char **argv;        /* Argument strings. */
  281. {
  282.     register Listbox *listPtr;
  283.     Tk_Window new;
  284.     Tk_Window tkwin = (Tk_Window) clientData;
  285.  
  286.     if (argc < 2) {
  287.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  288.         argv[0], " pathName ?options?\"", (char *) NULL);
  289.     return TCL_ERROR;
  290.     }
  291.  
  292.     new = Tk_CreateWindowFromPath(interp, tkwin, argv[1], (char *) NULL);
  293.     if (new == NULL) {
  294.     return TCL_ERROR;
  295.     }
  296.  
  297.     /*
  298.      * Initialize the fields of the structure that won't be initialized
  299.      * by ConfigureListbox, or that ConfigureListbox requires to be
  300.      * initialized already (e.g. resource pointers).
  301.      */
  302.  
  303.     listPtr = (Listbox *) ckalloc(sizeof(Listbox));
  304.     listPtr->tkwin = new;
  305.     listPtr->interp = interp;
  306.     listPtr->numElements = 0;
  307.     listPtr->elementPtr = NULL;
  308.     listPtr->normalBorder = NULL;
  309.     listPtr->fontPtr = NULL;
  310.     listPtr->fgColorPtr = NULL;
  311.     listPtr->textGC = None;
  312.     listPtr->selBorder = NULL;
  313.     listPtr->selFgColorPtr = NULL;
  314.     listPtr->selTextGC = NULL;
  315.     listPtr->geometry = NULL;
  316.     listPtr->topIndex = 0;
  317.     listPtr->xOffset = 0;
  318.     listPtr->selectFirst = -1;
  319.     listPtr->selectLast = -1;
  320.     listPtr->exportSelection = 1;
  321.     listPtr->cursor = None;
  322.     listPtr->yScrollCmd = NULL;
  323.     listPtr->xScrollCmd = NULL;
  324.     listPtr->flags = 0;
  325.  
  326.     Tk_SetClass(listPtr->tkwin, "Listbox");
  327.     Tk_CreateEventHandler(listPtr->tkwin, ExposureMask|StructureNotifyMask,
  328.         ListboxEventProc, (ClientData) listPtr);
  329.     Tk_CreateSelHandler(listPtr->tkwin, XA_STRING, ListboxFetchSelection,
  330.         (ClientData) listPtr, XA_STRING);
  331.     Tcl_CreateCommand(interp, Tk_PathName(listPtr->tkwin), ListboxWidgetCmd,
  332.         (ClientData) listPtr, (void (*)()) NULL);
  333.     if (ConfigureListbox(interp, listPtr, argc-2, argv+2, 0) != TCL_OK) {
  334.     goto error;
  335.     }
  336.  
  337.     interp->result = Tk_PathName(listPtr->tkwin);
  338.     return TCL_OK;
  339.  
  340.     error:
  341.     Tk_DestroyWindow(listPtr->tkwin);
  342.     return TCL_ERROR;
  343. }
  344.  
  345. /*
  346.  *--------------------------------------------------------------
  347.  *
  348.  * ListboxWidgetCmd --
  349.  *
  350.  *    This procedure is invoked to process the Tcl command
  351.  *    that corresponds to a widget managed by this module.
  352.  *    See the user documentation for details on what it does.
  353.  *
  354.  * Results:
  355.  *    A standard Tcl result.
  356.  *
  357.  * Side effects:
  358.  *    See the user documentation.
  359.  *
  360.  *--------------------------------------------------------------
  361.  */
  362.  
  363. static int
  364. ListboxWidgetCmd(clientData, interp, argc, argv)
  365.     ClientData clientData;        /* Information about listbox widget. */
  366.     Tcl_Interp *interp;            /* Current interpreter. */
  367.     int argc;                /* Number of arguments. */
  368.     char **argv;            /* Argument strings. */
  369. {
  370.     register Listbox *listPtr = (Listbox *) clientData;
  371.     int result = TCL_OK;
  372.     int length;
  373.     char c;
  374.  
  375.     if (argc < 2) {
  376.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  377.         argv[0], " option ?arg arg ...?\"", (char *) NULL);
  378.     return TCL_ERROR;
  379.     }
  380.     Tk_Preserve((ClientData) listPtr);
  381.     c = argv[1][0];
  382.     length = strlen(argv[1]);
  383.     if ((c == 'c') && (strncmp(argv[1], "configure", length) == 0)
  384.         && (length >= 2)) {
  385.     if (argc == 2) {
  386.         result = Tk_ConfigureInfo(interp, listPtr->tkwin, configSpecs,
  387.             (char *) listPtr, (char *) NULL, 0);
  388.     } else if (argc == 3) {
  389.         result = Tk_ConfigureInfo(interp, listPtr->tkwin, configSpecs,
  390.             (char *) listPtr, argv[2], 0);
  391.     } else {
  392.         result = ConfigureListbox(interp, listPtr, argc-2, argv+2,
  393.             TK_CONFIG_ARGV_ONLY);
  394.     }
  395.     } else if ((c == 'c') && (strncmp(argv[1], "curselection", length) == 0)
  396.         && (length >= 2)) {
  397.     int i;
  398.     char index[20];
  399.  
  400.     if (argc != 2) {
  401.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  402.             argv[0], " curselection\"",
  403.             (char *) NULL);
  404.         goto error;
  405.     }
  406.     if (listPtr->selectFirst != -1) {
  407.         for (i = listPtr->selectFirst; i <= listPtr->selectLast; i++) {
  408.         sprintf(index, "%d", i);
  409.         Tcl_AppendElement(interp, index, 0);
  410.         }
  411.     }
  412.     } else if ((c == 'd') && (strncmp(argv[1], "delete", length) == 0)) {
  413.     int first, last;
  414.  
  415.     if ((argc < 3) || (argc > 4)) {
  416.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  417.             argv[0], " delete firstIndex ?lastIndex?\"",
  418.             (char *) NULL);
  419.         goto error;
  420.     }
  421.     if (GetListboxIndex(interp, listPtr, argv[2], &first) != TCL_OK) {
  422.         goto error;
  423.     }
  424.     if (argc == 3) {
  425.         last = first;
  426.     } else {
  427.         if (GetListboxIndex(interp, listPtr, argv[3], &last) != TCL_OK) {
  428.         goto error;
  429.         }
  430.     }
  431.     DeleteEls(listPtr, first, last);
  432.     } else if ((c == 'g') && (strncmp(argv[1], "get", length) == 0)) {
  433.     int index;
  434.     register Element *elPtr;
  435.  
  436.     if (argc != 3) {
  437.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  438.             argv[0], " get index\"", (char *) NULL);
  439.         goto error;
  440.     }
  441.     if (GetListboxIndex(interp, listPtr, argv[2], &index) != TCL_OK) {
  442.         goto error;
  443.     }
  444.     if (index < 0) {
  445.         index = 0;
  446.     }
  447.     if (index >= listPtr->numElements) {
  448.         index = listPtr->numElements-1;
  449.     }
  450.     for (elPtr = listPtr->elementPtr; index > 0;
  451.         index--, elPtr = elPtr->nextPtr) {
  452.         /* Empty loop body. */
  453.     }
  454.     if (elPtr != NULL) {
  455.         interp->result = elPtr->text;
  456.     }
  457.     } else if ((c == 'i') && (strncmp(argv[1], "insert", length) == 0)) {
  458.     int index;
  459.  
  460.     if (argc < 3) {
  461.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  462.             argv[0], " insert index ?element? ?element ...?\"",
  463.             (char *) NULL);
  464.         goto error;
  465.     }
  466.     if (argc > 3) {
  467.         if (GetListboxIndex(interp, listPtr, argv[2], &index) != TCL_OK) {
  468.         goto error;
  469.         }
  470.         InsertEls(listPtr, index, argc-3, argv+3);
  471.     }
  472.     } else if ((c == 'n') && (strncmp(argv[1], "nearest", length) == 0)) {
  473.     int index, y;
  474.  
  475.     if (argc != 3) {
  476.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  477.             argv[0], " nearest y\"", (char *) NULL);
  478.         goto error;
  479.     }
  480.     if (Tcl_GetInt(interp, argv[2], &y) != TCL_OK) {
  481.         goto error;
  482.     }
  483.     index = NearestListboxElement(listPtr, y);
  484.     sprintf(interp->result, "%d", index);
  485.     } else if ((c == 's') && (length >= 2)
  486.         && (strncmp(argv[1], "scan", length) == 0)) {
  487.     int x, y;
  488.  
  489.     if (argc != 5) {
  490.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  491.             argv[0], " scan mark|dragto x y\"", (char *) NULL);
  492.         goto error;
  493.     }
  494.     if ((Tcl_GetInt(interp, argv[3], &x) != TCL_OK)
  495.         || (Tcl_GetInt(interp, argv[4], &y) != TCL_OK)) {
  496.         goto error;
  497.     }
  498.     if ((argv[2][0] == 'm')
  499.         && (strncmp(argv[2], "mark", strlen(argv[2])) == 0)) {
  500.         listPtr->scanMarkX = x;
  501.         listPtr->scanMarkY = y;
  502.         listPtr->scanMarkXOffset = listPtr->xOffset;
  503.         listPtr->scanMarkYIndex = listPtr->topIndex;
  504.     } else if ((argv[2][0] == 'd')
  505.         && (strncmp(argv[2], "dragto", strlen(argv[2])) == 0)) {
  506.         ListboxScanTo(listPtr, x, y);
  507.     } else {
  508.         Tcl_AppendResult(interp, "bad scan option \"", argv[2],
  509.             "\":  must be mark or dragto", (char *) NULL);
  510.         goto error;
  511.     }
  512.     } else if ((c == 's') && (length >= 2)
  513.         && (strncmp(argv[1], "select", length) == 0)) {
  514.     int index;
  515.  
  516.     if (argc < 3) {
  517.         Tcl_AppendResult(interp, "too few args: should be \"",
  518.             argv[0], " select option ?index?\"", (char *) NULL);
  519.         goto error;
  520.     }
  521.     length = strlen(argv[2]);
  522.     c = argv[2][0];
  523.     if ((c == 'c') && (argv[2] != NULL)
  524.         && (strncmp(argv[2], "clear", length) == 0)) {
  525.         if (argc != 3) {
  526.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  527.             argv[0], " select clear\"", (char *) NULL);
  528.         goto error;
  529.         }
  530.         if (listPtr->selectFirst != -1) {
  531.         ListboxRedrawRange(listPtr, listPtr->selectFirst,
  532.             listPtr->selectLast);
  533.         listPtr->selectFirst = -1;
  534.         }
  535.         goto done;
  536.     }
  537.     if (argc != 4) {
  538.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  539.             argv[0], " select option index\"", (char *) NULL);
  540.         goto error;
  541.     }
  542.     if (GetListboxIndex(interp, listPtr, argv[3], &index) != TCL_OK) {
  543.         goto error;
  544.     }
  545.     if ((c == 'a') && (strncmp(argv[2], "adjust", length) == 0)) {
  546.         if (index < (listPtr->selectFirst + listPtr->selectLast)/2) {
  547.         listPtr->selectAnchor = listPtr->selectLast;
  548.         } else {
  549.         listPtr->selectAnchor = listPtr->selectFirst;
  550.         }
  551.         ListboxSelectTo(listPtr, index);
  552.     } else if ((c == 'f') && (strncmp(argv[2], "from", length) == 0)) {
  553.         ListboxSelectFrom(listPtr, index);
  554.     } else if ((c == 't') && (strncmp(argv[2], "to", length) == 0)) {
  555.         ListboxSelectTo(listPtr, index);
  556.     } else {
  557.         Tcl_AppendResult(interp, "bad select option \"", argv[2],
  558.             "\": must be adjust, clear, from, or to", (char *) NULL);
  559.         goto error;
  560.     }
  561.     } else if ((c == 's') && (length >= 2)
  562.         && (strncmp(argv[1], "size", length) == 0)) {
  563.     sprintf(interp->result, "%d", listPtr->numElements);
  564.     } else if ((c == 'x') && (strncmp(argv[1], "xview", length) == 0)) {
  565.     int index;
  566.  
  567.     if (argc != 3) {
  568.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  569.             argv[0], " xview index\"", (char *) NULL);
  570.         goto error;
  571.     }
  572.     if (Tcl_GetInt(interp, argv[2], &index) != TCL_OK) {
  573.         goto error;
  574.     }
  575.     ChangeListboxOffset(listPtr, index*listPtr->xScrollUnit);
  576.     } else if ((c == 'y') && (strncmp(argv[1], "yview", length) == 0)) {
  577.     int index;
  578.  
  579.     if (argc != 3) {
  580.         Tcl_AppendResult(interp, "wrong # args: should be \"",
  581.             argv[0], " yview index\"", (char *) NULL);
  582.         goto error;
  583.     }
  584.     if (GetListboxIndex(interp, listPtr, argv[2], &index) != TCL_OK) {
  585.         goto error;
  586.     }
  587.     ChangeListboxView(listPtr, index);
  588.     } else {
  589.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  590.         "\": must be configure, curselection, delete, get, ",
  591.         "insert, nearest, scan, select, size, ",
  592.         "xview, or yview", (char *) NULL);
  593.     goto error;
  594.     }
  595.     done:
  596.     Tk_Release((ClientData) listPtr);
  597.     return result;
  598.  
  599.     error:
  600.     Tk_Release((ClientData) listPtr);
  601.     return TCL_ERROR;
  602. }
  603.  
  604. /*
  605.  *----------------------------------------------------------------------
  606.  *
  607.  * DestroyListbox --
  608.  *
  609.  *    This procedure is invoked by Tk_EventuallyFree or Tk_Release
  610.  *    to clean up the internal structure of a listbox at a safe time
  611.  *    (when no-one is using it anymore).
  612.  *
  613.  * Results:
  614.  *    None.
  615.  *
  616.  * Side effects:
  617.  *    Everything associated with the listbox is freed up.
  618.  *
  619.  *----------------------------------------------------------------------
  620.  */
  621.  
  622. static void
  623. DestroyListbox(clientData)
  624.     ClientData clientData;    /* Info about listbox widget. */
  625. {
  626.     register Listbox *listPtr = (Listbox *) clientData;
  627.     register Element *elPtr, *nextPtr;
  628.  
  629.     for (elPtr = listPtr->elementPtr; elPtr != NULL; ) {
  630.     nextPtr = elPtr->nextPtr;
  631.     ckfree((char *) elPtr);
  632.     elPtr = nextPtr;
  633.     }
  634.     if (listPtr->normalBorder != NULL) {
  635.     Tk_Free3DBorder(listPtr->normalBorder);
  636.     }
  637.     if (listPtr->fontPtr != NULL) {
  638.     Tk_FreeFontStruct(listPtr->fontPtr);
  639.     }
  640.     if (listPtr->fgColorPtr != NULL) {
  641.     Tk_FreeColor(listPtr->fgColorPtr);
  642.     }
  643.     if (listPtr->textGC != None) {
  644.     Tk_FreeGC(listPtr->textGC);
  645.     }
  646.     if (listPtr->selBorder != NULL) {
  647.     Tk_Free3DBorder(listPtr->selBorder);
  648.     }
  649.     if (listPtr->selFgColorPtr != NULL) {
  650.     Tk_FreeColor(listPtr->selFgColorPtr);
  651.     }
  652.     if (listPtr->selTextGC != None) {
  653.     Tk_FreeGC(listPtr->selTextGC);
  654.     }
  655.     if (listPtr->geometry != NULL) {
  656.     ckfree(listPtr->geometry);
  657.     }
  658.     if (listPtr->cursor != None) {
  659.     Tk_FreeCursor(listPtr->cursor);
  660.     }
  661.     if (listPtr->yScrollCmd != NULL) {
  662.     ckfree(listPtr->yScrollCmd);
  663.     }
  664.     if (listPtr->xScrollCmd != NULL) {
  665.     ckfree(listPtr->xScrollCmd);
  666.     }
  667.     ckfree((char *) listPtr);
  668. }
  669.  
  670. /*
  671.  *----------------------------------------------------------------------
  672.  *
  673.  * ConfigureListbox --
  674.  *
  675.  *    This procedure is called to process an argv/argc list, plus
  676.  *    the Tk option database, in order to configure (or reconfigure)
  677.  *    a listbox widget.
  678.  *
  679.  * Results:
  680.  *    The return value is a standard Tcl result.  If TCL_ERROR is
  681.  *    returned, then interp->result contains an error message.
  682.  *
  683.  * Side effects:
  684.  *    Configuration information, such as colors, border width,
  685.  *    etc. get set for listPtr;  old resources get freed,
  686.  *    if there were any.
  687.  *
  688.  *----------------------------------------------------------------------
  689.  */
  690.  
  691. static int
  692. ConfigureListbox(interp, listPtr, argc, argv, flags)
  693.     Tcl_Interp *interp;        /* Used for error reporting. */
  694.     register Listbox *listPtr;    /* Information about widget;  may or may
  695.                  * not already have values for some fields. */
  696.     int argc;            /* Number of valid entries in argv. */
  697.     char **argv;        /* Arguments. */
  698.     int flags;            /* Flags to pass to Tk_ConfigureWidget. */
  699. {
  700.     XGCValues gcValues;
  701.     GC new;
  702.     int width, height, fontHeight, oldExport;
  703.  
  704.     oldExport = listPtr->exportSelection;
  705.     if (Tk_ConfigureWidget(interp, listPtr->tkwin, configSpecs,
  706.         argc, argv, (char *) listPtr, flags) != TCL_OK) {
  707.     return TCL_ERROR;
  708.     }
  709.  
  710.     /*
  711.      * A few options need special processing, such as parsing the
  712.      * geometry and setting the background from a 3-D border.
  713.      */
  714.  
  715.     Tk_SetBackgroundFromBorder(listPtr->tkwin, listPtr->normalBorder);
  716.  
  717.     gcValues.foreground = listPtr->fgColorPtr->pixel;
  718.     gcValues.font = listPtr->fontPtr->fid;
  719.     gcValues.graphics_exposures = False;
  720.     new = Tk_GetGC(listPtr->tkwin, GCForeground|GCFont|GCGraphicsExposures,
  721.         &gcValues);
  722.     if (listPtr->textGC != None) {
  723.     Tk_FreeGC(listPtr->textGC);
  724.     }
  725.     listPtr->textGC = new;
  726.  
  727.     gcValues.foreground = listPtr->selFgColorPtr->pixel;
  728.     gcValues.font = listPtr->fontPtr->fid;
  729.     new = Tk_GetGC(listPtr->tkwin, GCForeground|GCFont, &gcValues);
  730.     if (listPtr->selTextGC != None) {
  731.     Tk_FreeGC(listPtr->selTextGC);
  732.     }
  733.     listPtr->selTextGC = new;
  734.  
  735.     /*
  736.      * Claim the selection if we've suddenly started exporting it.
  737.      */
  738.  
  739.     if (listPtr->exportSelection && (!oldExport)
  740.         && (listPtr->selectFirst !=-1)) {
  741.     Tk_OwnSelection(listPtr->tkwin, ListboxLostSelection,
  742.         (ClientData) listPtr);
  743.     }
  744.  
  745.     /*
  746.      * Register the desired geometry for the window, and arrange for
  747.      * the window to be redisplayed.
  748.      */
  749.  
  750.     if ((sscanf(listPtr->geometry, "%dx%d", &width, &height) != 2)
  751.         || (width <= 0) || (height <= 0)) {
  752.     Tcl_AppendResult(interp, "bad geometry \"",
  753.         listPtr->geometry, "\"", (char *) NULL);
  754.     return TCL_ERROR;
  755.     }
  756.     fontHeight = listPtr->fontPtr->ascent + listPtr->fontPtr->descent;
  757.     listPtr->lineHeight = fontHeight + 1 + 2*listPtr->selBorderWidth;
  758.     listPtr->numLines = (Tk_Height(listPtr->tkwin) - 2*listPtr->borderWidth)
  759.         / listPtr->lineHeight;
  760.     if (listPtr->numLines < 0) {
  761.     listPtr->numLines = 0;
  762.     }
  763.     ListboxComputeWidths(listPtr, 1);
  764.     width = (width+1)*listPtr->xScrollUnit + 2*listPtr->borderWidth
  765.         + 2*listPtr->selBorderWidth;
  766.     height = height*listPtr->lineHeight + 2*listPtr->borderWidth;
  767.     Tk_GeometryRequest(listPtr->tkwin, width, height);
  768.     Tk_SetInternalBorder(listPtr->tkwin, listPtr->borderWidth);
  769.     listPtr->flags |= UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR;
  770.     ListboxRedrawRange(listPtr, 0, listPtr->numElements-1);
  771.     return TCL_OK;
  772. }
  773.  
  774. /*
  775.  *--------------------------------------------------------------
  776.  *
  777.  * DisplayListbox --
  778.  *
  779.  *    This procedure redraws the contents of a listbox window.
  780.  *
  781.  * Results:
  782.  *    None.
  783.  *
  784.  * Side effects:
  785.  *    Information appears on the screen.
  786.  *
  787.  *--------------------------------------------------------------
  788.  */
  789.  
  790. static void
  791. DisplayListbox(clientData)
  792.     ClientData clientData;    /* Information about window. */
  793. {
  794.     register Listbox *listPtr = (Listbox *) clientData;
  795.     register Tk_Window tkwin = listPtr->tkwin;
  796.     register Element *elPtr;
  797.     GC gc;
  798.     int i, limit, x, y, margin;
  799.     Pixmap pixmap;
  800.  
  801.     listPtr->flags &= ~REDRAW_PENDING;
  802.     if (listPtr->flags & UPDATE_V_SCROLLBAR) {
  803.     ListboxUpdateVScrollbar(listPtr);
  804.     }
  805.     if (listPtr->flags & UPDATE_H_SCROLLBAR) {
  806.     ListboxUpdateHScrollbar(listPtr);
  807.     }
  808.     listPtr->flags &= ~(REDRAW_PENDING|UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR);
  809.     if ((listPtr->tkwin == NULL) || !Tk_IsMapped(tkwin)) {
  810.     return;
  811.     }
  812.  
  813.     /*
  814.      * Redrawing is done in a temporary pixmap that is allocated
  815.      * here and freed at the end of the procedure.  All drawing is
  816.      * done to the pixmap, and the pixmap is copied to the screen
  817.      * at the end of the procedure.  This provides the smoothest
  818.      * possible visual effects (no flashing on the screen).
  819.      */
  820.  
  821.     pixmap = XCreatePixmap(Tk_Display(tkwin), Tk_WindowId(tkwin),
  822.         Tk_Width(tkwin), Tk_Height(tkwin),
  823.         DefaultDepthOfScreen(Tk_Screen(tkwin)));
  824.     Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap, listPtr->normalBorder,
  825.         0, 0, Tk_Width(tkwin), Tk_Height(tkwin), listPtr->borderWidth,
  826.         listPtr->relief);
  827.  
  828.     /*
  829.      * Iterate through all of the elements of the listbox, displaying each
  830.      * in turn.  Selected elements use a different GC and have a raised
  831.      * background.
  832.      */
  833.  
  834.     limit = listPtr->topIndex + listPtr->numLines - 1;
  835.     if (limit >= listPtr->numElements) {
  836.     limit = listPtr->numElements-1;
  837.     }
  838.     margin = listPtr->selBorderWidth + listPtr->xScrollUnit/2;
  839.     for (elPtr = listPtr->elementPtr, i = 0; (elPtr != NULL) && (i <= limit);
  840.         elPtr = elPtr->nextPtr, i++) {
  841.     if (i < listPtr->topIndex) {
  842.         continue;
  843.     }
  844.     x = listPtr->borderWidth;
  845.     y = ((i - listPtr->topIndex) * listPtr->lineHeight) 
  846.         + listPtr->borderWidth;
  847.     gc = listPtr->textGC;
  848.     if ((listPtr->selectFirst >= 0) && (i >= listPtr->selectFirst)
  849.         && (i <= listPtr->selectLast)) {
  850.         gc = listPtr->selTextGC;
  851.         Tk_Fill3DRectangle(Tk_Display(tkwin), pixmap,
  852.             listPtr->selBorder, x, y,
  853.             Tk_Width(tkwin) - 2*listPtr->borderWidth,
  854.             listPtr->lineHeight, listPtr->selBorderWidth,
  855.             TK_RELIEF_RAISED);
  856.     }
  857.     y += listPtr->fontPtr->ascent + listPtr->selBorderWidth;
  858.     x += margin - elPtr->lBearing - listPtr->xOffset;
  859.     XDrawString(Tk_Display(tkwin), pixmap, gc, x, y,
  860.         elPtr->text, elPtr->textLength);
  861.     }
  862.  
  863.     /*
  864.      * Redraw the border for the listbox to make sure that it's on top
  865.      * of any of the text of the listbox entries.
  866.      */
  867.  
  868.     Tk_Draw3DRectangle(Tk_Display(tkwin), pixmap,
  869.         listPtr->normalBorder, 0, 0, Tk_Width(tkwin),
  870.         Tk_Height(tkwin), listPtr->borderWidth,
  871.         listPtr->relief);
  872.     XCopyArea(Tk_Display(tkwin), pixmap, Tk_WindowId(tkwin),
  873.         listPtr->textGC, 0, 0, Tk_Width(tkwin), Tk_Height(tkwin),
  874.         0, 0);
  875.     XFreePixmap(Tk_Display(tkwin), pixmap);
  876. }
  877.  
  878. /*
  879.  *----------------------------------------------------------------------
  880.  *
  881.  * InsertEls --
  882.  *
  883.  *    Add new elements to a listbox widget.
  884.  *
  885.  * Results:
  886.  *    None.
  887.  *
  888.  * Side effects:
  889.  *    New information gets added to listPtr;  it will be redisplayed
  890.  *    soon, but not immediately.
  891.  *
  892.  *----------------------------------------------------------------------
  893.  */
  894.  
  895. static void
  896. InsertEls(listPtr, index, argc, argv)
  897.     register Listbox *listPtr;    /* Listbox that is to get the new
  898.                  * elements. */
  899.     int index;            /* Add the new elements before this
  900.                  * element. */
  901.     int argc;            /* Number of new elements to add. */
  902.     char **argv;        /* New elements (one per entry). */
  903. {
  904.     register Element *prevPtr, *newPtr;
  905.     int length, dummy, i, oldMaxWidth;
  906.     XCharStruct bbox;
  907.  
  908.     /*
  909.      * Find the element before which the new ones will be inserted.
  910.      */
  911.  
  912.     if (index <= 0) {
  913.     index = 0;
  914.     }
  915.     if (index > listPtr->numElements) {
  916.     index = listPtr->numElements;
  917.     }
  918.     if (index == 0) {
  919.     prevPtr = NULL;
  920.     } else {
  921.     for (prevPtr = listPtr->elementPtr, i = index - 1; i > 0; i--) {
  922.         prevPtr = prevPtr->nextPtr;
  923.     }
  924.     }
  925.  
  926.     /*
  927.      * For each new element, create a record, initialize it, and link
  928.      * it into the list of elements.
  929.      */
  930.  
  931.     oldMaxWidth = listPtr->maxWidth;
  932.     for (i = argc ; i > 0; i--, argv++, prevPtr = newPtr) {
  933.     length = strlen(*argv);
  934.     newPtr = (Element *) ckalloc(ElementSize(length));
  935.     newPtr->textLength = length;
  936.     strcpy(newPtr->text, *argv);
  937.     XTextExtents(listPtr->fontPtr, newPtr->text, newPtr->textLength,
  938.         &dummy, &dummy, &dummy, &bbox);
  939.     newPtr->lBearing = bbox.lbearing;
  940.     newPtr->pixelWidth = bbox.lbearing + bbox.rbearing;
  941.     if (newPtr->pixelWidth > listPtr->maxWidth) {
  942.         listPtr->maxWidth = newPtr->pixelWidth;
  943.     }
  944.     if (prevPtr == NULL) {
  945.         newPtr->nextPtr = listPtr->elementPtr;
  946.         listPtr->elementPtr = newPtr;
  947.     } else {
  948.         newPtr->nextPtr = prevPtr->nextPtr;
  949.         prevPtr->nextPtr = newPtr;
  950.     }
  951.     }
  952.     listPtr->numElements += argc;
  953.  
  954.     /*
  955.      * Update the selection to account for the  renumbering that has just
  956.      * occurred.  Then arrange for the new information to be displayed.
  957.      */
  958.  
  959.     if (index <= listPtr->selectFirst) {
  960.     listPtr->selectFirst += argc;
  961.     }
  962.     if (index <= listPtr->selectLast) {
  963.     listPtr->selectLast += argc;
  964.     }
  965.     listPtr->flags |= UPDATE_V_SCROLLBAR;
  966.     if (listPtr->maxWidth != oldMaxWidth) {
  967.     listPtr->flags |= UPDATE_H_SCROLLBAR;
  968.     }
  969.     ListboxRedrawRange(listPtr, index, listPtr->numElements-1);
  970. }
  971.  
  972. /*
  973.  *----------------------------------------------------------------------
  974.  *
  975.  * DeleteEls --
  976.  *
  977.  *    Remove one or more elements from a listbox widget.
  978.  *
  979.  * Results:
  980.  *    None.
  981.  *
  982.  * Side effects:
  983.  *    Memory gets freed, the listbox gets modified and (eventually)
  984.  *    redisplayed.
  985.  *
  986.  *----------------------------------------------------------------------
  987.  */
  988.  
  989. static void
  990. DeleteEls(listPtr, first, last)
  991.     register Listbox *listPtr;    /* Listbox widget to modify. */
  992.     int first;            /* Index of first element to delete. */
  993.     int last;            /* Index of last element to delete. */
  994. {
  995.     register Element *prevPtr, *elPtr;
  996.     int count, i, widthChanged;
  997.  
  998.     /*
  999.      * Adjust the range to fit within the existing elements of the
  1000.      * listbox, and make sure there's something to delete.
  1001.      */
  1002.  
  1003.     if (first < 0) {
  1004.     first = 0;
  1005.     }
  1006.     if (last >= listPtr->numElements) {
  1007.     last = listPtr->numElements-1;
  1008.     }
  1009.     count = last + 1 - first;
  1010.     if (count <= 0) {
  1011.     return;
  1012.     }
  1013.  
  1014.     /*
  1015.      * Find the element just before the ones to delete.
  1016.      */
  1017.  
  1018.     if (first == 0) {
  1019.     prevPtr = NULL;
  1020.     } else {
  1021.     for (i = first-1, prevPtr = listPtr->elementPtr; i > 0; i--) {
  1022.         prevPtr = prevPtr->nextPtr;
  1023.     }
  1024.     }
  1025.  
  1026.     /*
  1027.      * Delete the requested number of elements.
  1028.      */
  1029.  
  1030.     widthChanged = 0;
  1031.     for (i = count; i > 0; i--) {
  1032.     if (prevPtr == NULL) {
  1033.         elPtr = listPtr->elementPtr;
  1034.         listPtr->elementPtr = elPtr->nextPtr;
  1035.     } else {
  1036.         elPtr = prevPtr->nextPtr;
  1037.         prevPtr->nextPtr = elPtr->nextPtr;
  1038.     }
  1039.     if (elPtr->pixelWidth == listPtr->maxWidth) {
  1040.         widthChanged = 1;
  1041.     }
  1042.     ckfree((char *) elPtr);
  1043.     }
  1044.     listPtr->numElements -= count;
  1045.  
  1046.     /*
  1047.      * Update the selection and viewing information to reflect the change
  1048.      * in the element numbering, and redisplay to slide information up over
  1049.      * the elements that were deleted.
  1050.      */
  1051.  
  1052.     if (first <= listPtr->selectFirst) {
  1053.     listPtr->selectFirst -= count;
  1054.     if (listPtr->selectFirst < first) {
  1055.         listPtr->selectFirst = first;
  1056.     }
  1057.     }
  1058.     if (first <= listPtr->selectLast) {
  1059.     listPtr->selectLast -= count;
  1060.     if (listPtr->selectLast < first) {
  1061.         listPtr->selectLast = first-1;
  1062.     }
  1063.     }
  1064.     if (listPtr->selectLast < listPtr->selectFirst) {
  1065.     listPtr->selectFirst = -1;
  1066.     }
  1067.     if (first <= listPtr->topIndex) {
  1068.     listPtr->topIndex -= count;
  1069.     if (listPtr->topIndex < first) {
  1070.         listPtr->topIndex = first;
  1071.     }
  1072.     }
  1073.     listPtr->flags |= UPDATE_V_SCROLLBAR;
  1074.     if (widthChanged) {
  1075.     ListboxComputeWidths(listPtr, 0);
  1076.     listPtr->flags |= UPDATE_H_SCROLLBAR;
  1077.     }
  1078.     ListboxRedrawRange(listPtr, first, listPtr->numElements-1);
  1079. }
  1080.  
  1081. /*
  1082.  *--------------------------------------------------------------
  1083.  *
  1084.  * ListboxEventProc --
  1085.  *
  1086.  *    This procedure is invoked by the Tk dispatcher for various
  1087.  *    events on listboxes.
  1088.  *
  1089.  * Results:
  1090.  *    None.
  1091.  *
  1092.  * Side effects:
  1093.  *    When the window gets deleted, internal structures get
  1094.  *    cleaned up.  When it gets exposed, it is redisplayed.
  1095.  *
  1096.  *--------------------------------------------------------------
  1097.  */
  1098.  
  1099. static void
  1100. ListboxEventProc(clientData, eventPtr)
  1101.     ClientData clientData;    /* Information about window. */
  1102.     XEvent *eventPtr;        /* Information about event. */
  1103. {
  1104.     Listbox *listPtr = (Listbox *) clientData;
  1105.  
  1106.     if (eventPtr->type == Expose) {
  1107.     ListboxRedrawRange(listPtr,
  1108.         NearestListboxElement(listPtr, eventPtr->xexpose.y),
  1109.         NearestListboxElement(listPtr, eventPtr->xexpose.y
  1110.         + eventPtr->xexpose.height));
  1111.     } else if (eventPtr->type == DestroyNotify) {
  1112.     Tcl_DeleteCommand(listPtr->interp, Tk_PathName(listPtr->tkwin));
  1113.     listPtr->tkwin = NULL;
  1114.     if (listPtr->flags & REDRAW_PENDING) {
  1115.         Tk_CancelIdleCall(DisplayListbox, (ClientData) listPtr);
  1116.     }
  1117.     Tk_EventuallyFree((ClientData) listPtr, DestroyListbox);
  1118.     } else if (eventPtr->type == ConfigureNotify) {
  1119.     Tk_Preserve((ClientData) listPtr);
  1120.     listPtr->numLines = (Tk_Height(listPtr->tkwin)
  1121.         - 2*listPtr->borderWidth) / listPtr->lineHeight;
  1122.     listPtr->flags |= UPDATE_V_SCROLLBAR|UPDATE_H_SCROLLBAR;
  1123.     ListboxRedrawRange(listPtr, 0, listPtr->numElements-1);
  1124.     Tk_Release((ClientData) listPtr);
  1125.     }
  1126. }
  1127.  
  1128. /*
  1129.  *--------------------------------------------------------------
  1130.  *
  1131.  * GetListboxIndex --
  1132.  *
  1133.  *    Parse an index into a listbox and return either its value
  1134.  *    or an error.
  1135.  *
  1136.  * Results:
  1137.  *    A standard Tcl result.  If all went well, then *indexPtr is
  1138.  *    filled in with the index (into listPtr) corresponding to
  1139.  *    string.  Otherwise an error message is left in interp->result.
  1140.  *
  1141.  * Side effects:
  1142.  *    None.
  1143.  *
  1144.  *--------------------------------------------------------------
  1145.  */
  1146.  
  1147. static int
  1148. GetListboxIndex(interp, listPtr, string, indexPtr)
  1149.     Tcl_Interp *interp;        /* For error messages. */
  1150.     Listbox *listPtr;        /* Listbox for which the index is being
  1151.                  * specified. */
  1152.     char *string;        /* Numerical index into listPtr's element
  1153.                  * list, or "end" to refer to last element. */
  1154.     int *indexPtr;        /* Where to store converted index. */
  1155. {
  1156.     if (string[0] == 'e') {
  1157.     if (strncmp(string, "end", strlen(string)) != 0) {
  1158.         badIndex:
  1159.         Tcl_AppendResult(interp, "bad listbox index \"", string,
  1160.             "\"", (char *) NULL);
  1161.         return TCL_ERROR;
  1162.     }
  1163.     *indexPtr = listPtr->numElements;
  1164.     if (listPtr->numElements <= 0) {
  1165.         *indexPtr = 0;
  1166.     }
  1167.     } else {
  1168.     if (Tcl_GetInt(interp, string, indexPtr) != TCL_OK) {
  1169.         Tcl_ResetResult(interp);
  1170.         goto badIndex;
  1171.     }
  1172.     }
  1173.     return TCL_OK;
  1174. }
  1175.  
  1176. /*
  1177.  *----------------------------------------------------------------------
  1178.  *
  1179.  * ChangeListboxView --
  1180.  *
  1181.  *    Change the view on a listbox widget.
  1182.  *
  1183.  * Results:
  1184.  *    None.
  1185.  *
  1186.  * Side effects:
  1187.  *    What's displayed on the screen is changed.  If there is a
  1188.  *    scrollbar associated with this widget, then the scrollbar
  1189.  *    is instructed to change its display too.
  1190.  *
  1191.  *----------------------------------------------------------------------
  1192.  */
  1193.  
  1194. static void
  1195. ChangeListboxView(listPtr, index)
  1196.     register Listbox *listPtr;        /* Information about widget. */
  1197.     int index;                /* Index of element in listPtr. */
  1198. {
  1199.     if (listPtr->tkwin == NULL) {
  1200.     return;
  1201.     }
  1202.  
  1203.     if (index >= listPtr->numElements) {
  1204.     index = listPtr->numElements-1;
  1205.     }
  1206.     if (index < 0) {
  1207.     index = 0;
  1208.     }
  1209.     if (listPtr->topIndex != index) {
  1210.     if (!(listPtr->flags & REDRAW_PENDING)) {
  1211.         Tk_DoWhenIdle(DisplayListbox, (ClientData) listPtr);
  1212.         listPtr->flags |= REDRAW_PENDING;
  1213.     }
  1214.     listPtr->topIndex = index;
  1215.     ListboxUpdateVScrollbar(listPtr);
  1216.     }
  1217. }
  1218.  
  1219. /*
  1220.  *----------------------------------------------------------------------
  1221.  *
  1222.  * ChangListboxOffset --
  1223.  *
  1224.  *    Change the horizontal offset for a listbox.
  1225.  *
  1226.  * Results:
  1227.  *    None.
  1228.  *
  1229.  * Side effects:
  1230.  *    The listbox may be redrawn to reflect its new horizontal
  1231.  *    offset.
  1232.  *
  1233.  *----------------------------------------------------------------------
  1234.  */
  1235.  
  1236. static void
  1237. ChangeListboxOffset(listPtr, offset)
  1238.     register Listbox *listPtr;        /* Information about widget. */
  1239.     int offset;                /* Desired new "xOffset" for
  1240.                      * listbox. */
  1241. {
  1242.     int maxOffset;
  1243.  
  1244.     if (listPtr->tkwin == NULL) {
  1245.     return;
  1246.     }
  1247.  
  1248.     /*
  1249.      * Make sure that the new offset is within the allowable range, and
  1250.      * round it off to an even multiple of xScrollUnit.
  1251.      */
  1252.  
  1253.     maxOffset = listPtr->maxWidth + (listPtr->xScrollUnit-1)
  1254.         - (Tk_Width(listPtr->tkwin) - 2*listPtr->borderWidth
  1255.         - 2*listPtr->selBorderWidth - listPtr->xScrollUnit);
  1256.     if (offset > maxOffset) {
  1257.     offset = maxOffset;
  1258.     }
  1259.     if (offset < 0) {
  1260.     offset = 0;
  1261.     }
  1262.     offset -= offset%listPtr->xScrollUnit;
  1263.     if (offset != listPtr->xOffset) {
  1264.     listPtr->xOffset = offset;
  1265.     listPtr->flags |= UPDATE_H_SCROLLBAR;
  1266.     ListboxRedrawRange(listPtr, 0, listPtr->numElements);
  1267.     }
  1268. }
  1269.  
  1270. /*
  1271.  *----------------------------------------------------------------------
  1272.  *
  1273.  * ListboxScanTo --
  1274.  *
  1275.  *    Given a point (presumably of the curent mouse location)
  1276.  *    drag the view in the window to implement the scan operation.
  1277.  *
  1278.  * Results:
  1279.  *    None.
  1280.  *
  1281.  * Side effects:
  1282.  *    The view in the window may change.
  1283.  *
  1284.  *----------------------------------------------------------------------
  1285.  */
  1286.  
  1287. static void
  1288. ListboxScanTo(listPtr, x, y)
  1289.     register Listbox *listPtr;        /* Information about widget. */
  1290.     int x;                /* X-coordinate to use for scan
  1291.                      * operation. */
  1292.     int y;                /* Y-coordinate to use for scan
  1293.                      * operation. */
  1294. {
  1295.     int newTopIndex, newOffset;
  1296.  
  1297.     /*
  1298.      * Compute new top line for screen by amplifying the difference
  1299.      * between the current position and the place where the scan
  1300.      * started (the "mark" position).  If we run off the top or bottom
  1301.      * of the list, then reset the mark point so that the current
  1302.      * position continues to correspond to the edge of the window.
  1303.      * This means that the picture will start dragging as soon as the
  1304.      * mouse reverses direction (without this reset, might have to slide
  1305.      * mouse a long ways back before the picture starts moving again).
  1306.      */
  1307.  
  1308.     newTopIndex = listPtr->scanMarkYIndex
  1309.         - (10*(y - listPtr->scanMarkY))/listPtr->lineHeight;
  1310.     if (newTopIndex >= listPtr->numElements) {
  1311.     newTopIndex = listPtr->scanMarkYIndex = listPtr->numElements-1;
  1312.     listPtr->scanMarkY = y;
  1313.     } else if (newTopIndex < 0) {
  1314.     newTopIndex = listPtr->scanMarkYIndex = 0;
  1315.     listPtr->scanMarkY = y;
  1316.     }
  1317.     ChangeListboxView(listPtr, newTopIndex);
  1318.  
  1319.     /*
  1320.      * Compute new left edge for display in a similar fashion by amplifying
  1321.      * the difference between the current position and the place where the
  1322.      * scan started.
  1323.      */
  1324.  
  1325.     newOffset = listPtr->scanMarkXOffset - (10*(x - listPtr->scanMarkX));
  1326.     if (newOffset >= listPtr->maxWidth) {
  1327.     newOffset = listPtr->scanMarkXOffset = listPtr->maxWidth;
  1328.     listPtr->scanMarkX = x;
  1329.     } else if (newOffset < 0) {
  1330.     newOffset = listPtr->scanMarkXOffset = 0;
  1331.     listPtr->scanMarkX = x;
  1332.     }
  1333.     ChangeListboxOffset(listPtr, newOffset);
  1334. }
  1335.  
  1336. /*
  1337.  *----------------------------------------------------------------------
  1338.  *
  1339.  * NearestListboxElement --
  1340.  *
  1341.  *    Given a y-coordinate inside a listbox, compute the index of
  1342.  *    the element under that y-coordinate (or closest to that
  1343.  *    y-coordinate).
  1344.  *
  1345.  * Results:
  1346.  *    The return value is an index of an element of listPtr.  If
  1347.  *    listPtr has no elements, then 0 is always returned.
  1348.  *
  1349.  * Side effects:
  1350.  *    None.
  1351.  *
  1352.  *----------------------------------------------------------------------
  1353.  */
  1354.  
  1355. static int
  1356. NearestListboxElement(listPtr, y)
  1357.     register Listbox *listPtr;        /* Information about widget. */
  1358.     int y;                /* Y-coordinate in listPtr's window. */
  1359. {
  1360.     int index;
  1361.  
  1362.     index = (y - listPtr->borderWidth)/listPtr->lineHeight;
  1363.     if (index >= listPtr->numLines) {
  1364.     index = listPtr->numLines-1;
  1365.     }
  1366.     if (index < 0) {
  1367.     index = 0;
  1368.     }
  1369.     index += listPtr->topIndex;
  1370.     if (index >= listPtr->numElements) {
  1371.     index = listPtr->numElements-1;
  1372.     }
  1373.     return index;
  1374. }
  1375.  
  1376. /*
  1377.  *----------------------------------------------------------------------
  1378.  *
  1379.  * ListboxSelectFrom --
  1380.  *
  1381.  *    Start a new selection in a listbox.
  1382.  *
  1383.  * Results:
  1384.  *    None.
  1385.  *
  1386.  * Side effects:
  1387.  *    ListPtr claims the selection, and the selection becomes the
  1388.  *    single element given by index.
  1389.  *
  1390.  *----------------------------------------------------------------------
  1391.  */
  1392.  
  1393. static void
  1394. ListboxSelectFrom(listPtr, index)
  1395.     register Listbox *listPtr;        /* Information about widget. */
  1396.     int index;                /* Index of element that is to
  1397.                      * become the new selection. */
  1398. {
  1399.     /*
  1400.      * Make sure the index is within the proper range for the listbox.
  1401.      */
  1402.  
  1403.     if (index <= 0) {
  1404.     index = 0;
  1405.     }
  1406.     if (index >= listPtr->numElements) {
  1407.     index = listPtr->numElements-1;
  1408.     }
  1409.  
  1410.     if (listPtr->selectFirst != -1) {
  1411.     ListboxRedrawRange(listPtr, listPtr->selectFirst, listPtr->selectLast);
  1412.     } else if (listPtr->exportSelection) {
  1413.     Tk_OwnSelection(listPtr->tkwin, ListboxLostSelection,
  1414.         (ClientData) listPtr);
  1415.     }
  1416.  
  1417.     listPtr->selectFirst = listPtr->selectLast = index;
  1418.     listPtr->selectAnchor = index;
  1419.     ListboxRedrawRange(listPtr, index, index);
  1420. }
  1421.  
  1422. /*
  1423.  *----------------------------------------------------------------------
  1424.  *
  1425.  * ListboxSelectTo --
  1426.  *
  1427.  *    Modify the selection by moving its un-anchored end.  This could
  1428.  *    make the selection either larger or smaller.
  1429.  *
  1430.  * Results:
  1431.  *    None.
  1432.  *
  1433.  * Side effects:
  1434.  *    The selection changes.
  1435.  *
  1436.  *----------------------------------------------------------------------
  1437.  */
  1438.  
  1439. static void
  1440. ListboxSelectTo(listPtr, index)
  1441.     register Listbox *listPtr;        /* Information about widget. */
  1442.     int index;                /* Index of element that is to
  1443.                      * become the "other" end of the
  1444.                      * selection. */
  1445. {
  1446.     int newFirst, newLast;
  1447.  
  1448.     /*
  1449.      * Make sure the index is within the proper range for the listbox.
  1450.      */
  1451.  
  1452.     if (index <= 0) {
  1453.     index = 0;
  1454.     }
  1455.     if (index >= listPtr->numElements) {
  1456.     index = listPtr->numElements-1;
  1457.     }
  1458.  
  1459.     /*
  1460.      * We should already own the selection, but grab it if we don't.
  1461.      */
  1462.  
  1463.     if (listPtr->selectFirst == -1) {
  1464.     ListboxSelectFrom(listPtr, index);
  1465.     }
  1466.  
  1467.     if (listPtr->selectAnchor < index) {
  1468.     newFirst = listPtr->selectAnchor;
  1469.     newLast = index;
  1470.     } else {
  1471.     newFirst = index;
  1472.     newLast = listPtr->selectAnchor;
  1473.     }
  1474.     if ((listPtr->selectFirst == newFirst)
  1475.         && (listPtr->selectLast == newLast)) {
  1476.     return;
  1477.     }
  1478.     if (listPtr->selectFirst != newFirst) {
  1479.     if (listPtr->selectFirst < newFirst) {
  1480.         ListboxRedrawRange(listPtr, listPtr->selectFirst, newFirst-1);
  1481.     } else {
  1482.         ListboxRedrawRange(listPtr, newFirst, listPtr->selectFirst-1);
  1483.     }
  1484.     listPtr->selectFirst = newFirst;
  1485.     }
  1486.     if (listPtr->selectLast != newLast) {
  1487.     if (listPtr->selectLast < newLast) {
  1488.         ListboxRedrawRange(listPtr, listPtr->selectLast+1, newLast);
  1489.     } else {
  1490.         ListboxRedrawRange(listPtr, newLast+1, listPtr->selectLast);
  1491.     }
  1492.     listPtr->selectLast = newLast;
  1493.     }
  1494. }
  1495.  
  1496. /*
  1497.  *----------------------------------------------------------------------
  1498.  *
  1499.  * ListboxFetchSelection --
  1500.  *
  1501.  *    This procedure is called back by Tk when the selection is
  1502.  *    requested by someone.  It returns part or all of the selection
  1503.  *    in a buffer provided by the caller.
  1504.  *
  1505.  * Results:
  1506.  *    The return value is the number of non-NULL bytes stored
  1507.  *    at buffer.  Buffer is filled (or partially filled) with a
  1508.  *    NULL-terminated string containing part or all of the selection,
  1509.  *    as given by offset and maxBytes.  The selection is returned
  1510.  *    as a Tcl list with one list element for each element in the
  1511.  *    listbox.
  1512.  *
  1513.  * Side effects:
  1514.  *    None.
  1515.  *
  1516.  *----------------------------------------------------------------------
  1517.  */
  1518.  
  1519. static int
  1520. ListboxFetchSelection(clientData, offset, buffer, maxBytes)
  1521.     ClientData clientData;        /* Information about listbox widget. */
  1522.     int offset;                /* Offset within selection of first
  1523.                      * byte to be returned. */
  1524.     char *buffer;            /* Location in which to place
  1525.                      * selection. */
  1526.     int maxBytes;            /* Maximum number of bytes to place
  1527.                      * at buffer, not including terminating
  1528.                      * NULL character. */
  1529. {
  1530.     register Listbox *listPtr = (Listbox *) clientData;
  1531.     register Element *elPtr;
  1532.     char **argv, *selection;
  1533.     int src, dst, length, count, argc;
  1534.  
  1535.     if ((listPtr->selectFirst == -1) || !listPtr->exportSelection) {
  1536.     return -1;
  1537.     }
  1538.  
  1539.     /*
  1540.      * Use Tcl_Merge to format the listbox elements into a suitable
  1541.      * Tcl list.
  1542.      */
  1543.  
  1544.     argc = listPtr->selectLast - listPtr->selectFirst + 1;
  1545.     argv = (char **) ckalloc((unsigned) (argc*sizeof(char *)));
  1546.     for (src = 0, dst = 0, elPtr = listPtr->elementPtr; ;
  1547.         src++, elPtr = elPtr->nextPtr) {
  1548.     if (src < listPtr->selectFirst) {
  1549.         continue;
  1550.     }
  1551.     if (src > listPtr->selectLast) {
  1552.         break;
  1553.     }
  1554.     argv[dst] = elPtr->text;
  1555.     dst++;
  1556.     }
  1557.     selection = Tcl_Merge(argc, argv);
  1558.  
  1559.     /*
  1560.      * Copy the requested portion of the selection to the buffer.
  1561.      */
  1562.  
  1563.     length = strlen(selection);
  1564.     count = length - offset;
  1565.     if (count <= 0) {
  1566.     count = 0;
  1567.     goto done;
  1568.     }
  1569.     if (count > maxBytes) {
  1570.     count = maxBytes;
  1571.     }
  1572.     memcpy((VOID *) buffer, (VOID *) (selection + offset), count);
  1573.  
  1574.     done:
  1575.     buffer[count] = '\0';
  1576.     ckfree(selection);
  1577.     ckfree((char *) argv);
  1578.     return count;
  1579. }
  1580.  
  1581. /*
  1582.  *----------------------------------------------------------------------
  1583.  *
  1584.  * ListboxLostSelection --
  1585.  *
  1586.  *    This procedure is called back by Tk when the selection is
  1587.  *    grabbed away from a listbox widget.
  1588.  *
  1589.  * Results:
  1590.  *    None.
  1591.  *
  1592.  * Side effects:
  1593.  *    The existing selection is unhighlighted, and the window is
  1594.  *    marked as not containing a selection.
  1595.  *
  1596.  *----------------------------------------------------------------------
  1597.  */
  1598.  
  1599. static void
  1600. ListboxLostSelection(clientData)
  1601.     ClientData clientData;        /* Information about listbox widget. */
  1602. {
  1603.     register Listbox *listPtr = (Listbox *) clientData;
  1604.  
  1605.     if ((listPtr->selectFirst >= 0) && listPtr->exportSelection) {
  1606.     ListboxRedrawRange(listPtr, listPtr->selectFirst, listPtr->selectLast);
  1607.     listPtr->selectFirst = -1;
  1608.     }
  1609. }
  1610.  
  1611. /*
  1612.  *----------------------------------------------------------------------
  1613.  *
  1614.  * ListboxRedrawRange --
  1615.  *
  1616.  *    Ensure that a given range of elements is eventually redrawn on
  1617.  *    the display (if those elements in fact appear on the display).
  1618.  *
  1619.  * Results:
  1620.  *    None.
  1621.  *
  1622.  * Side effects:
  1623.  *    Information gets redisplayed.
  1624.  *
  1625.  *----------------------------------------------------------------------
  1626.  */
  1627.  
  1628.     /* ARGSUSED */
  1629. static void
  1630. ListboxRedrawRange(listPtr, first, last)
  1631.     register Listbox *listPtr;        /* Information about widget. */
  1632.     int first;                /* Index of first element in list
  1633.                      * that needs to be redrawn. */
  1634.     int last;                /* Index of last element in list
  1635.                      * that needs to be redrawn.  May
  1636.                      * be less than first;
  1637.                      * these just bracket a range. */
  1638. {
  1639.     if ((listPtr->tkwin == NULL) || !Tk_IsMapped(listPtr->tkwin)
  1640.         || (listPtr->flags & REDRAW_PENDING)) {
  1641.     return;
  1642.     }
  1643.     Tk_DoWhenIdle(DisplayListbox, (ClientData) listPtr);
  1644.     listPtr->flags |= REDRAW_PENDING;
  1645. }
  1646.  
  1647. /*
  1648.  *----------------------------------------------------------------------
  1649.  *
  1650.  * ListboxUpdateVScrollbar --
  1651.  *
  1652.  *    This procedure is invoked whenever information has changed in
  1653.  *    a listbox in a way that would invalidate a vertical scrollbar
  1654.  *    display.  If there is an associated scrollbar, then this command
  1655.  *    updates it by invoking a Tcl command.
  1656.  *
  1657.  * Results:
  1658.  *    None.
  1659.  *
  1660.  * Side effects:
  1661.  *    A Tcl command is invoked, and an additional command may be
  1662.  *    invoked to process errors in the command.
  1663.  *
  1664.  *----------------------------------------------------------------------
  1665.  */
  1666.  
  1667. static void
  1668. ListboxUpdateVScrollbar(listPtr)
  1669.     register Listbox *listPtr;        /* Information about widget. */
  1670. {
  1671.     char string[60];
  1672.     int result, last;
  1673.  
  1674.     if (listPtr->yScrollCmd == NULL) {
  1675.     return;
  1676.     }
  1677.     last = listPtr->topIndex + listPtr->numLines - 1;
  1678.     if (last >= listPtr->numElements) {
  1679.     last = listPtr->numElements-1;
  1680.     }
  1681.     if (last < listPtr->topIndex) {
  1682.     last = listPtr->topIndex;
  1683.     }
  1684.     sprintf(string, " %d %d %d %d", listPtr->numElements, listPtr->numLines,
  1685.         listPtr->topIndex, last);
  1686.     result = Tcl_VarEval(listPtr->interp, listPtr->yScrollCmd, string,
  1687.         (char *) NULL);
  1688.     if (result != TCL_OK) {
  1689.     TkBindError(listPtr->interp);
  1690.     }
  1691. }
  1692.  
  1693. /*
  1694.  *----------------------------------------------------------------------
  1695.  *
  1696.  * ListboxUpdateHScrollbar --
  1697.  *
  1698.  *    This procedure is invoked whenever information has changed in
  1699.  *    a listbox in a way that would invalidate a horizontal scrollbar
  1700.  *    display.  If there is an associated horizontal scrollbar, then
  1701.  *    this command updates it by invoking a Tcl command.
  1702.  *
  1703.  * Results:
  1704.  *    None.
  1705.  *
  1706.  * Side effects:
  1707.  *    A Tcl command is invoked, and an additional command may be
  1708.  *    invoked to process errors in the command.
  1709.  *
  1710.  *----------------------------------------------------------------------
  1711.  */
  1712.  
  1713. static void
  1714. ListboxUpdateHScrollbar(listPtr)
  1715.     register Listbox *listPtr;        /* Information about widget. */
  1716. {
  1717.     char string[60];
  1718.     int result, totalUnits, windowUnits, first, last;
  1719.  
  1720.     if (listPtr->xScrollCmd == NULL) {
  1721.     return;
  1722.     }
  1723.     totalUnits = 1 + (listPtr->maxWidth-1)/listPtr->xScrollUnit;
  1724.     windowUnits = 1 + (Tk_Width(listPtr->tkwin)
  1725.         - 2*(listPtr->borderWidth + listPtr->selBorderWidth)-1)
  1726.         /listPtr->xScrollUnit;
  1727.     first = listPtr->xOffset/listPtr->xScrollUnit;
  1728.     last = first + windowUnits - 1;
  1729.     if (last < first) {
  1730.     last = first;
  1731.     }
  1732.     sprintf(string, " %d %d %d %d", totalUnits, windowUnits, first, last);
  1733.     result = Tcl_VarEval(listPtr->interp, listPtr->xScrollCmd, string,
  1734.         (char *) NULL);
  1735.     if (result != TCL_OK) {
  1736.     TkBindError(listPtr->interp);
  1737.     }
  1738. }
  1739.  
  1740. /*
  1741.  *----------------------------------------------------------------------
  1742.  *
  1743.  * ListboxComputeWidths --
  1744.  *
  1745.  *    This procedure is invoked to completely recompute width
  1746.  *    information used for displaying listboxes and for horizontal
  1747.  *    scrolling.
  1748.  *
  1749.  * Results:
  1750.  *    None.
  1751.  *
  1752.  * Side effects:
  1753.  *    If "fontChanged" is non-zero then the widths of the individual
  1754.  *    elements are all recomputed.  In addition, listPtr->maxWidth is
  1755.  *    recomputed.
  1756.  *
  1757.  *----------------------------------------------------------------------
  1758.  */
  1759.  
  1760. static void
  1761. ListboxComputeWidths(listPtr, fontChanged)
  1762.     Listbox *listPtr;        /* Listbox whose geometry is to be
  1763.                  * recomputed. */
  1764.     int fontChanged;        /* Non-zero means the font may have changed
  1765.                  * so per-element width information also
  1766.                  * has to be computed. */
  1767. {
  1768.     register Element *elPtr;
  1769.     int dummy;
  1770.     XCharStruct bbox;
  1771.  
  1772.     listPtr->xScrollUnit = XTextWidth(listPtr->fontPtr, "0", 1);
  1773.     listPtr->maxWidth = 0;
  1774.     for (elPtr = listPtr->elementPtr; elPtr != NULL; elPtr = elPtr->nextPtr) {
  1775.     if (fontChanged) {
  1776.         XTextExtents(listPtr->fontPtr, elPtr->text, elPtr->textLength,
  1777.             &dummy, &dummy, &dummy, &bbox);
  1778.         elPtr->lBearing = bbox.lbearing;
  1779.         elPtr->pixelWidth = bbox.lbearing + bbox.rbearing;
  1780.     }
  1781.     if (elPtr->pixelWidth > listPtr->maxWidth) {
  1782.         listPtr->maxWidth = elPtr->pixelWidth;
  1783.     }
  1784.     }
  1785. }
  1786.